using System;
using System.Text.RegularExpressions;

namespace OrbitOne.EmailProtector
{

    internal class XTea : IEncryptor
    {
        #region Ctor
        public XTea()
        {
        }
        #endregion Ctor


        #region IEncrypt Implementation
        public string Encrypt(string data, string key)
        {
            if (data.Length == 0)
                throw new ArgumentException("Data must be at least 1 character in length.");

            uint[] formattedKey = FormatKey(key);

            string cipher = string.Empty;
            uint[] tempData = new uint[2];
            for (int i = 0; i < data.Length; i += 8)
            {
                tempData[0] = Util.ConvertBytesToUInt(Get4CharsByteArray(data, i));
                tempData[1] = Util.ConvertBytesToUInt(Get4CharsByteArray(data, i + 4));
                Code(tempData, formattedKey);
                cipher += Util.ConvertUIntToString(tempData[0]) + Util.ConvertUIntToString(tempData[1]);
            }

            return Escape(cipher);
        }


        public string Decrypt(string cipher, string key)
        {
            uint[] formattedKey = FormatKey(key);

            int x = 0;
            uint[] tempData = new uint[2];
            byte[] dataBytes = new byte[cipher.Length / 8 * 2];
            for (int i = 0; i < cipher.Length; i += 8)
            {
                tempData[0] = Util.ConvertStringToUInt(cipher.Substring(i, 4));
                tempData[1] = Util.ConvertStringToUInt(cipher.Substring(i + 4, 4));
                Decode(tempData, formattedKey);
                dataBytes[x++] = (byte)tempData[0];
                dataBytes[x++] = (byte)tempData[1];
            }
            string decipheredString = System.Text.ASCIIEncoding.ASCII.GetString(dataBytes, 0, dataBytes.Length);
            if (decipheredString[decipheredString.Length - 1] == '\0')
                decipheredString = decipheredString.Substring(0, decipheredString.Length - 1);
            return decipheredString;
        }

        public string GetEncryptJavascriptFunctions()
        {
            return Util.GetResource("OrbitOne.EmailProtector.XTea.js");
        }
        #endregion IEncrypt Implementation


        #region Private Methods
        private uint[] FormatKey(string Key)
        {
            if (Key.Length == 0)
                throw new ArgumentException("Key must be between 1 and 16 characters in length");

            Key = Key.PadRight(16, Convert.ToChar(0)).Substring(0, 16); // Ensure that the key is 16 chars in length.

            uint[] formattedKey = new uint[4];

            // Get the key into the correct format for TEA usage.
            int j = 0;
            for (int i = 0; i < Key.Length; i += 4)
                formattedKey[j++] = Util.ConvertStringToUInt(Key.Substring(i, 4));

            return formattedKey;
        }




        private byte[] Get4CharsByteArray(string str, int startIndex)
        {
            byte[] result = new byte[] { };
            if (startIndex > str.Length)
                return result;
            string subString = (startIndex + 4 > str.Length) ? str.Substring(startIndex) : str.Substring(startIndex, 4);
            result = System.Text.ASCIIEncoding.ASCII.GetBytes(subString);
            return result;

        }



        #region XTea Algorithm

        private void Code(uint[] v, uint[] k)
        {
            uint y = v[0];
            uint z = v[1];
            uint sum = 0;
            uint delta = 0x9E3779B9;
            uint n = 32;

            while (sum != delta * 32)
            {
                y += (z << 4 ^ z >> 5) + z ^ sum + k[sum & 3];
                sum += delta;
                z += (y << 4 ^ y >> 5) + y ^ sum + k[sum >> 11 & 3];
            }

            v[0] = y;
            v[1] = z;
        }

        private void Decode(uint[] v, uint[] k)
        {
            uint y = v[0];
            uint z = v[1];

            uint delta = 0x9E3779B9;
            uint n = 32;
            uint sum = delta * 32;


            while (sum != 0)
            {
                z -= (y << 4 ^ y >> 5) + y ^ sum + k[sum >> 11 & 3];
                sum -= delta;
                y -= (z << 4 ^ z >> 5) + z ^ sum + k[sum & 3];
            }

            v[0] = y;
            v[1] = z;
        }

      
        #endregion
		
		
		private string Escape(string str)
        {
            Regex regex = new Regex(@"[\0\t\n\v\f\r\xa0'""!\\]");
            MatchEvaluator escapeEvaluator = new MatchEvaluator(this.EscapeReplacement);
            return regex.Replace(str, escapeEvaluator);
        }



        public string EscapeReplacement(Match m)
        {
            return "!" + (int)(m.Groups[0].Captures[0].Value[0]) + "!";
        }
        #endregion Private Methods
    }

}